---
title: 垃圾回收
description: 垃圾回收
date: 2022-02-28 15:19:58
---
import Alert from '@/components/post/alert.astro'

[[toc]]

## 概述

程序运行过程中需要不断地为进程分配新的内存

但是计算机的内存空间是有限的，内存总会有被占满的时候

所以需要进行**垃圾回收**

## 自动回收算法

### 标记清除

沿着**指针**找到所有对象，将其标记为**活动对象**

**将内存中未被标记的对象删除**，释放内存空间

<Alert type="info">**算法简单易实现，但是<span class="text-red-600">会产生大量的内存碎片</span>**</Alert>

### 复制

将整个空间**平均分成 `from` 和 `to` 两部分**

先**在 `from` 空间进行内存分配**，当**空间被占满时，标记活动对象并复制到 `to` 空间**

**复制完成之后，将两空间互换**

**优点：**
- 吞吐量高
- 没有内存碎片

**缺点：**
- 复制操作存在时间成本问题
- **空间等分导致可用的内存空间直接减半**

### 引用计数

**实时统计指向对象的引用数(指针数量)**

**当引用数为 0 时，实时回收对象**

**优点：**
- 对程序影响时间短，效率高
- 高性能实时回收

**缺点：**
- **循环引用可能会导致内存溢出**

### 标记压缩

沿着**指针**找到所有对象，将其标记为**活动对象**

**将所有活动对象集中并移到内存的一端**

**直接清理掉边界之外的内存，释放连续空间**

**优点：**
- 无内存碎片
- 可用内存空间不变

**缺点：**
- 清除和整理的操作比较麻烦
- 需要对整个堆进行多次搜索，堆越大，耗时越长

### 代际假设和分代收集

> 大多数对象的生命周期都很短，很快就不再被需要了；那些一直存活的对象通常会存在很久

**可以将对象进行分代，对不同的分代实施不同的垃圾回收算法，以达到更高的效率**

## JS 中的垃圾回收

### 栈的垃圾回收

**通过下移 ESP 来完成垃圾回收**

<Alert type="info" title="ESP(Extended Stack Pointer)">**扩展栈指针寄存器，用于存放函数的栈顶指针**</Alert>

函数执行过程中，**会将其上下文压入栈中，`ESP` 上移**

**当函数执行完成之后，其执行上下文就可以被销毁了**

**当下一个函数入栈时，会直接覆盖之前未被销毁的空间**

### 堆的垃圾回收

**使用垃圾回收器，分代收集**

引擎将堆的空间分为**新生代**和**老生代**，并对两个区域实施不同的垃圾回收策略

**新生代：**
- 用于存放生存时间较短的对象
- 大多数新创建的小的对象都会被分配到该区域
- **使用复制算法执行垃圾回收**
- **垃圾回收较为频繁**
- 空间较小，约为 1~8 M

**老生代：**
- 用于存放生存时间长的对象和大对象
- **新生代中经历两次垃圾回收之后依然存在的对象会被移入老生代**
- **使用标记压缩算法执行垃圾回收**

> 垃圾回收算法和脚本任务执行在同一线程，**为避免垃圾回收影响应用的性能，V8 引擎将标记的过程拆分为多个子标记，让垃圾回收和应用逻辑交替执行以保证用户体验**